﻿#include "stdafx.h"

#include "svgcanvas.h"
#include "svgwriter.h"
#include "utils.h"

#include <gsl/span>

SvgCanvas::SvgCanvas(double width, double height)
	: m_width(width)
	, m_height(height)
{
}

void SvgCanvas::Paint(QPainter& painter, const QRect& viewPort)
{
	if (viewPort != m_viewPort)
	{
		QRect adjustViewPort = viewPort;
		const QPoint& tl = viewPort.topLeft();
		if (tl.x() < m_basePoint.x() && tl.y() < m_basePoint.y()) // 如果原点在 basePoint 左上角，以 basePoint 为原点
			adjustViewPort.setTopLeft(m_basePoint);
		else if (tl.x() < m_basePoint.x())
			adjustViewPort.setLeft(m_basePoint.x());
		else if (tl.y() < m_basePoint.y())
			adjustViewPort.setTop(m_basePoint.y());

		if (adjustViewPort != m_viewPort)
		{
			m_viewPort = adjustViewPort;
			RePaint();
		}
	}
	painter.drawPixmap(m_viewPort.topLeft(), m_cache);
}

void SvgCanvas::RePaint()
{
	if (m_groupInfo)
	{
		_RePaintGroupPixmap();
		GroupRePaint();
		return;
	}
	const int width = std::min(m_viewPort.width(), static_cast<int>(m_width * m_scale));
	const int height = std::min(m_viewPort.height(), static_cast<int>(m_height * m_scale));
	if (m_cache.size() != QSize(width, height))
		m_cache = QPixmap(width, height);

	_PaintLocal(m_viewPort, m_cache);
}

void SvgCanvas::RePaint(QRectF canvasRect)
{
	canvasRect = EnlargeRect(canvasRect, 3);
	const QRect widgetRect = _CanvasRectToWidgetRect(canvasRect).toRect();

	QPixmap image(static_cast<int>(widgetRect.width()), static_cast<int>(widgetRect.height()));

	_PaintLocal(widgetRect, image);

	QPainter cachePainter(&m_cache);
	cachePainter.drawPixmap(widgetRect.topLeft() - m_viewPort.topLeft(), image);
}

void SvgCanvas::RePaint(const QRectF& rect1, const QRectF& rect2)
{
	RePaint(rect1.united(rect2));
}

void SvgCanvas::SetBackground(const QColor& color)
{
	m_background = color;
}

void SvgCanvas::SetShapes(SvgShapeVec shapes)
{
	m_shapeVec = std::move(shapes);
}

void SvgCanvas::SetScale(double scale)
{
	m_scale = scale;
}

void SvgCanvas::SetBasePoint(const QPoint basePoint)
{
	m_basePoint = basePoint;
}

QPoint SvgCanvas::GetBasePoint() const
{
	return m_basePoint;
}

double SvgCanvas::Height() const
{
	return m_height;
}

double SvgCanvas::Width() const
{
	return m_width;
}

QColor SvgCanvas::Background() const
{
	return m_background;
}

void SvgCanvas::Clear()
{
	m_groupInfo = nullptr;
	m_shapeVec.clear();
}

bool SvgCanvas::Save(const QString& path) const
{
	return SvgWriter::Save(m_shapeVec, path, m_background, m_height, m_width);
}

QPointF SvgCanvas::WidgetPosToCanvasPos(const QPointF& p) const
{
	return (p - m_basePoint) / m_scale;
}

QPointF SvgCanvas::CanvasPosToWidgetPos(const QPointF& p) const
{
	return (p * m_scale) + m_basePoint;
}

void SvgCanvas::MakeGroup(const vector<SvgShape*>& group)
{
	m_groupInfo = std::make_unique<GroupInfo>(group);

	for (size_t i = 0; i < m_shapeVec.size(); ++i)
	{
		if (m_shapeVec[i].get() == group[0])
			m_groupInfo->groupMinIdx = i;
		if (m_shapeVec[i].get() == group[group.size() - 1])
		{
			m_groupInfo->groupMaxIdx = i;
			break;
		}
	}
	_RePaintGroupPixmap();
}

void SvgCanvas::TranslateGroup(const QRectF& rect)
{
	for (const auto shape : m_groupInfo->group)
		shape->Translate(rect);
	GroupRePaint();
}

void SvgCanvas::TranslateGroup(const QPointF& pos)
{
	for (const auto shape : m_groupInfo->group)
		shape->Translate(pos);
	GroupRePaint();
}

void SvgCanvas::ClearGroup()
{
	m_groupInfo = nullptr;
}

void SvgCanvas::PainterToViewPortPos(QPainter& painter) const
{
	_InitPainter(painter, m_viewPort);
}

QRect SvgCanvas::GetViewPort() const
{
	return m_viewPort;
}

QRectF SvgCanvas::_CanvasRectToWidgetRect(const QRectF& rect) const
{
	return { CanvasPosToWidgetPos(rect.topLeft()), QSizeF{rect.width() * m_scale, rect.height() * m_scale} };
}

QRectF SvgCanvas::_WidgetRectToCanvasRect(const QRectF& rect) const
{
	return { WidgetPosToCanvasPos(rect.topLeft()), QSizeF{rect.width() / m_scale, rect.height() / m_scale} };
}

void SvgCanvas::_InitPainter(QPainter& painter, const QRectF& viewPort) const
{
	painter.translate(-viewPort.topLeft() + m_basePoint); // 以 m_basePoint 的位置绘制
	painter.setRenderHint(QPainter::Antialiasing, true);
	painter.scale(m_scale, m_scale);
}

QRectF SvgCanvas::_CanvasViewPort() const
{
	return _WidgetRectToCanvasRect(m_viewPort);
}

void SvgCanvas::_PaintLocal(const QRect& viewPort, QPixmap& target) const
{
	{
		QPainter painter(&target);
		painter.fillRect(0, 0, target.width(), target.height(), m_background);
	}

	std::vector<SvgShape*> needPaint;
	const QRectF canvasViewPort = _WidgetRectToCanvasRect(viewPort);
	for (const auto& shape : m_shapeVec)
	{
		if (shape->VisualRect().intersects(canvasViewPort))
			needPaint.push_back(shape.get());
	}

	ParallelPaint(needPaint, target, [&viewPort, this](QPainter& painter) { _InitPainter(painter, viewPort); });
}

void SvgCanvas::_RePaintGroupPixmap()
{
	if (m_groupInfo->groupMinIdx != 0)
	{
		if (m_groupInfo->lowPixmap.size() != m_cache.size())
			m_groupInfo->lowPixmap = QPixmap(m_cache.size());
		m_groupInfo->lowPixmap.fill(Qt::transparent);
		ParallelPaint(gsl::make_span(&m_shapeVec[0], m_groupInfo->groupMinIdx), // todo: 其实这里，还有其他几个地方可以，判断下图形是否再 m_viewPort 中
			m_groupInfo->lowPixmap,
			[this](QPainter& painter) { PainterToViewPortPos(painter); });
	}

	if (m_groupInfo->groupMaxIdx != m_shapeVec.size() - 1)
	{
		if (m_groupInfo->highPixmap.size() != m_cache.size())
			m_groupInfo->highPixmap = QPixmap(m_cache.size());
		m_groupInfo->highPixmap.fill(Qt::transparent);
		ParallelPaint(
			gsl::make_span(&m_shapeVec[m_groupInfo->groupMaxIdx + 1], m_shapeVec.size() - m_groupInfo->groupMaxIdx - 1),
			m_groupInfo->highPixmap,
			[this](QPainter& painter) { PainterToViewPortPos(painter); });
	}
}

void SvgCanvas::GroupRePaint()
{
	m_cache.fill(m_background);
	if (m_groupInfo->groupMinIdx != 0)
	{
		QPainter painter(&m_cache);
		painter.drawPixmap(0, 0, m_groupInfo->lowPixmap);
	}

	// 左闭右开
	ParallelPaint(gsl::make_span(&m_shapeVec[m_groupInfo->groupMinIdx], &m_shapeVec[m_groupInfo->groupMaxIdx] + 1),
		m_cache,
		[this](QPainter& painter) { PainterToViewPortPos(painter); });

	if (m_groupInfo->groupMaxIdx != m_shapeVec.size() - 1)
	{
		QPainter painter(&m_cache);
		painter.drawPixmap(0, 0, m_groupInfo->highPixmap);
	}
}

void SvgCanvas::AddShape(unique_ptr<SvgShape> shape)
{
	QPainter painter(&m_cache);
	_InitPainter(painter, m_viewPort);
	shape->Paint(painter);
	m_shapeVec.push_back(std::move(shape));
}

void SvgCanvas::DeleteShape(SvgShape* shape)
{
	const auto iter = std::find_if(m_shapeVec.begin(),
		m_shapeVec.end(),
		[shape](const unique_ptr<SvgShape>& p) { return p.get() == shape; });
	if (iter != m_shapeVec.end())
		m_shapeVec.erase(iter);
}

// 下面几个，如果传参改成拷贝，找到一个删除 shapes 中一个，会不会效率高一点, 或者考虑用 set?
void SvgCanvas::DeleteShapes(const vector<SvgShape*>& shapes)
{
	for (size_t slow = 0, fast = 0; fast < m_shapeVec.size(); ++fast)
	{
		SvgShape* shape = m_shapeVec[fast].get();
		auto iter = std::find(shapes.begin(), shapes.end(), shape);
		if (iter == shapes.end())
		{
			m_shapeVec[slow] = std::move(m_shapeVec[fast]);
			++slow;
		}
	}
	m_shapeVec.resize(m_shapeVec.size() - shapes.size());
}

void SvgCanvas::SendShapesToBack(const vector<SvgShape*>& shapes)
{
	SvgShapeVec selected;
	for (int slow = m_shapeVec.size() - 1, fast = m_shapeVec.size() - 1; fast >= 0; --fast)
	{
		SvgShape* shape = m_shapeVec[fast].get();
		auto iter = std::find(shapes.begin(), shapes.end(), shape);
		if (iter == shapes.end())
		{
			m_shapeVec[slow] = std::move(m_shapeVec[fast]);
			--slow;
		}
		else
		{
			selected.push_back(std::move(m_shapeVec[fast]));
		}
	}
	QRectF needRepaintRect = selected[0]->VisualRect();
	ASSERT(selected.size() == shapes.size());
	for (int i = 0, j = selected.size() - 1; i < selected.size(); ++i, --j)
	{
		needRepaintRect = needRepaintRect.united(selected[j]->VisualRect());
		m_shapeVec[i] = std::move(selected[j]);
	}
	RePaint(needRepaintRect);
}

void SvgCanvas::BringShapesToFront(const vector<SvgShape*>& shapes)
{
	SvgShapeVec selected;
	for (int slow = 0, fast = 0; fast < m_shapeVec.size(); ++fast)
	{
		SvgShape* shape = m_shapeVec[fast].get();
		auto iter = std::find(shapes.begin(), shapes.end(), shape);
		if (iter == shapes.end())
		{
			m_shapeVec[slow] = std::move(m_shapeVec[fast]);
			++slow;
		}
		else
		{
			selected.push_back(std::move(m_shapeVec[fast]));
		}
	}
	ASSERT(selected.size() == shapes.size());

	QRectF needRepaintRect = selected[0]->VisualRect();
	const size_t offset = m_shapeVec.size() - selected.size();
	for (int i = 0; i < selected.size(); ++i)
	{
		needRepaintRect = needRepaintRect.united(selected[i]->VisualRect());
		m_shapeVec[offset + i] = std::move(selected[i]);
	}
	RePaint(needRepaintRect);
}

SvgShape* SvgCanvas::GetShapeByCoordinate(QPointF pos) const
{
	const int size = m_shapeVec.size();
	for (int i = size - 1; i >= 0; --i)
	{
		if (m_shapeVec[i]->PointInShape(pos))
			return m_shapeVec[i].get();
	}
	return nullptr;
}

vector<SvgShape*> SvgCanvas::GetShapeByRect(const QRectF& rect) const
{
	vector<SvgShape*> shapes;
	for (const auto& shape : m_shapeVec)
	{
		QRectF visualRect = shape->VisualRect();
		if (visualRect.intersects(rect))
			shapes.push_back(shape.get());
	}
	return shapes;
}

SvgShape* SvgCanvas::GetLastShape() const
{
	if (m_shapeVec.empty())
		return nullptr;
	return m_shapeVec.back().get();
}

void SvgCanvas::ReSize(double width, double height)
{
	m_width = width;
	m_height = height;
}

void SvgCanvas::SetHeight(double height)
{
	m_height = height;
}

void SvgCanvas::SetWidth(double width)
{
	m_width = width;
}

